export type DataType = "string" | "number" | "list" | "custom";


/** 读取数据的辅助工具 */
export const ReadFileHelp = {

    readByte(src: Blob, offset: number = 0): Promise<number> {
        return new Promise((resolve, reject) => {
            let reader = new FileReader();
            reader.onload = ev => {
                resolve(
                    reader.result instanceof ArrayBuffer
                        ? new Uint8Array(reader.result)[0]
                        : 0
                );
            }
            if (offset + 1 < src.size) {
                reader.readAsArrayBuffer(src.slice(offset, offset + 1));
            }
            else { resolve(0); }
        });
    },

    async *readByteIterator(src: Blob, start_offset: number = 0) {
        let index = start_offset;
        let to_end = src.size - 1;
        let reader = new FileReader();
        reader.onload = ev => getOneByte(
            reader.result instanceof ArrayBuffer
                ? new Uint8Array(reader.result)[0]
                : 0
        );

        let getOneByte: (value: number) => void;
        let readOneByte = () => new Promise<number>((resolve, reject) => {
            getOneByte = resolve;
            reader.readAsArrayBuffer(src.slice(index, index + 1))
        });

        while (index < to_end) {
            yield await readOneByte();
            index++;
        }
    },

    readBinaryString(src: Blob): Promise<string | null> {
        return new Promise((resolve, reject) => {
            let reader = new FileReader();
            reader.onload = ev => {
                resolve(typeof reader.result == "string" ? reader.result : null);
            }
            //reader.readAsArrayBuffer(blob);
            reader.readAsBinaryString(src);
            //reader.readAsDataURL(blob);
            //reader.readAsText(blob, "utf8");
        });
    },

    readText(src: Blob, encoding?: string): Promise<string> {
        return new Promise((resolve, reject) => {
            let reader = new FileReader();
            reader.onload = ev => {
                resolve(typeof reader.result == "string" ? reader.result : "");
            }
            reader.readAsText(src, encoding);
        });
    },

    /**
     * 读取文本，直到遇到 "\0"
     * @param src 数据源
     * @param encoding 文本编码方式（默认为 utf-8）
     * @param extend_callback 返回读取长度的回调
     */
    async readText_Until0(src: Blob, encoding?: string, extend_callback?: (byte_length?: number) => void): Promise<string> {
        let arr_until0 = [];
        for await (const b of this.readByteIterator(src, 0)) {
            if (b == 0) break;
            arr_until0.push(b);
        }
        if (extend_callback) {
            extend_callback(arr_until0.length);
        }
        let arr_byte = new Uint8Array(arr_until0);
        return await this.readText(new Blob([arr_byte]), encoding);
    },

    readArrayBuffer(src: Blob): Promise<ArrayBuffer | null> {

        return new Promise((resolve, reject) => {
            let reader = new FileReader();
            reader.onload = ev => {
                resolve(reader.result instanceof ArrayBuffer ? reader.result : null);
            }
            reader.readAsArrayBuffer(src);

        });
    }
}

interface BaseDataNodeSetOption {
    data_source?: Blob | ArrayBuffer
    offset?: number
    length?: number
    name?: string
}

export abstract class BaseDataNode /*implements IBaseDataNode*/ {
    name = "";

    /** 数据源 */
    data_source?: Blob | ArrayBuffer;
    /** 偏移量 */
    offset = 0;
    /** 长度 */
    length = 0;
    /** 值 */
    value?: any;//object | number | string;
    /**
     * 是否已经读取。
     * 如果需要重新读取数据，请将该值设置为 false
     */
    is_read = false;

    /** 数据类型 */
    abstract get data_type(): DataType;

    /** 设置属性 */
    _set(option: BaseDataNodeSetOption) {
        Object.assign(this, option);
        return this;
    }

    /** 在开始解析前 */
    onBeforeAnalyze?: <TNode extends this>(node: TNode) => Promise<void>

    /** 在完成解析后 */
    onAfterAnalyze?: <TNode extends this>(node: TNode) => Promise<void>

    onValueChange?: () => void

    /** 返回子节点 */
    abstract get inner(): BaseDataNode[];
    /** 返回同一级的下一个节点 */
    //abstract after(): Promise<BaseDataNode | undefined>;

    /** 从 Blob 中获取数据，返回结果 */
    protected abstract readFromBlob(blob: Blob): Promise<this["value"]>;
    /** 从 ArrayBuffer 中获取数据，返回结果 */
    protected abstract readFromBuffer(buffer: ArrayBuffer): Promise<this["value"]>;
    /** 解析处理，主要为实现数据读取，子节点的创建等操作 */
    protected abstract doAnalyze(): Promise<void>;

    /** 数据的获取操作 */
    async readValue() {
        if (!this.is_read) {
            let val = undefined;
            if (this.data_source instanceof Blob) {
                val = await this.readFromBlob(this.data_source);
            }
            else if (this.data_source instanceof ArrayBuffer) {
                val = await this.readFromBuffer(this.data_source);
            }
            this.value = val;
            this.is_read = true;
            if (this.onValueChange) this.onValueChange();
        }
    };

    /** 进行解析 */
    async analyze() {
        if (this.data_source) {
            if (this.onBeforeAnalyze) await this.onBeforeAnalyze(this);
            await this.doAnalyze()
            if (this.onAfterAnalyze) await this.onAfterAnalyze(this);
        }
        else {
            console.log("this.data_source is null");
        }
    }

    toString() {
        return this.value || this.value.toString ? this.value.toString() : "";
    }
}

/** 有列表的节点 */
export abstract class BaseCollection<TItem extends BaseDataNode> extends BaseDataNode {
    get data_type(): DataType { return "list"; }

    /** 每页大小 */
    page_size = 20
    page_index = 0
    /** 元素的数量 */
    //item_count = 0
    /** 是否已经无法读取到更多的元素 */
    is_nomore_item = false

    /** 最近可能未读取元素的索引。如果元素已经获取完毕，该索引将没有对应元素 */
    last_index = 0
    /** 最近可能未读取元素的偏移量（以当前节点的偏移为参考点）。如果该偏移已经超出数据切片的范围，一般表示该节点已经全部获取 */
    last_offset = 0

    items: TItem[] = []
    value?: TItem[]

    get inner(): TItem[] { return this.value || []; }
    //get inner(): TItem[] { return this.items || []; }

    /** 是否可以跳转到第一页 */
    get page_can_first(): boolean { return this.page_index > 0; }
    /** 是否可以明确跳转到最后一页 */
    abstract get page_can_last(): boolean
    /** 是否可以跳转到上一页 */
    get page_can_prev(): boolean { return this.page_index > 0; }
    /** 是否可以跳转到下一页 */
    get page_can_next(): boolean {
        return this.page_index < Math.ceil(this.items.length / this.page_size) || !this.is_nomore_item;
    }

    protected async readFromBlob(blob: Blob): Promise<this["value"]> {
        return this.readPage(blob);
    }
    protected async readFromBuffer(buffer: ArrayBuffer): Promise<this["value"]> {
        return this.readPage(buffer);
    }

    private async readPage(src: Blob | ArrayBuffer): Promise<this["value"]> {
        let ret: TItem[] = [];

        let index_from = this.page_index * this.page_size;
        let index_to = (this.page_index + 1) * this.page_size - 1;
        for (let i = index_from; i <= index_to; i++) {
            if (!this.items[i]) {
                let reading_offset = this.getItemOffset(i);
                if (reading_offset) {
                    // 正常按顺序索引
                    let { item, next_offset } = await this.createItem(src, reading_offset);
                    this.items[i] = item;
                    this.last_index = i + 1;
                    this.last_offset = next_offset ? next_offset - this.offset : this.length;
                    if (!next_offset || next_offset >= this.offset + this.length) {
                        // 没有更多节点了
                        this.is_nomore_item = true;
                        break;
                    }
                }
                else {
                    break;
                }
            }
            ret.push(this.items[i]);
        }

        return ret;
    }

    /**
     * 获取一个相应索引的元素的偏移量
     * @param index 
     */
    protected abstract getItemOffset(index: number): number | undefined

    /**
     * 创建一个元素，
     * @param src 
     * @param offset 
     */
    protected abstract async createItem(src: Blob | ArrayBuffer, offset: number): Promise<{ item: TItem, next_offset?: number }>

    async pageToFirst() {
        if (this.data_source && this.page_can_first) {
            this.page_index = 0;
            this.is_read = false;
            await this.readValue();
            return true;
        }
    }
    async pageToLast() {
        if (this.data_source && this.page_can_last) {
            this.page_index = Math.max(0, Math.ceil(this.items.length / this.page_size) - 1);
            this.is_read = false;
            await this.readValue();
            return true;
        }
    }
    async pageToPrev() {
        if (this.data_source && this.page_can_prev) {
            this.page_index -= 1;
            this.is_read = false;
            await this.readValue();
            return true;
        }
    }
    async pageToNext() {
        if (this.data_source && this.page_can_next) {
            this.page_index += 1;
            this.is_read = false;
            await this.readValue();
            return true;
        }
    }

    protected async doAnalyze(): Promise<void> {
        if (this.data_source) {
            this.page_index = 0;
            //this.item_count = Math.floor(this.length / this.item_size);
            await this.analyze();
        }
    }
}